/**
 * 点位/建筑节点.
 * 需要使用的属性有:
 * + bg背景
 * + location定位点
 * + areaType区域类型
 * + area区域描述
 * 
 * areaType区域类型:
 * + point: {location:[10,20],areaType:'point'}
 * + circle: {location:[10,20],area:30,areaType:'circle'}
 * + rectangle: {location:[10,20],area:[30,60],areaType:'rectangle'}
 * + polygon: {location:[10,20],area:[[30,60],[20,70],[15,40]],areaType:'polygon'}
 * location为第一点,
 * 
 * 添加逻辑:
 * + 点,point
 *  - 点击按钮切换到创建状态, 点击地图释放后创建成功, 点击释放后就取消创建状态.
 * + 圆,circle
 *  - 点击按钮切换到创建圆状态, 第一次点击选定圆心, 移动鼠标时改变圆半径, 再次点击后确定圆半径, 并完成创建状态.
 * + 长方形,rectangle
 *  - 同圆, 支持两种模式, 按住并拖动和点击移动二次点击.
 * + 多边形,polygon
 * 参考:
 * 1. 添加移动删除Markers<https://tomickigrzegorz.github.io/react-leaflet-examples/#/add-move-and-delete-marker>
 */
import * as React from 'react';
// import { Handle, Position } from 'reactflow';
import max from 'lodash/max';
import min from 'lodash/min';
import { drag } from 'd3-drag';
import { select } from 'd3-selection';
import './node-position.css';
import { AreaType, PositionType } from '../types';
import { Icon } from '../widgets/icon';
// import { useUpdateNodeInternals } from 'reactflow';

export interface NodePositionOnChangeInnerArgs {
  data?: Partial<PositionType>;
  attr: 'area' | 'location';
  attrIndex: number;
  clientX: number;
  clientY: number;
  [key: string]: any;
}
export type NodePositionInnerChangeHandler = (args: NodePositionOnChangeInnerArgs) => void;


interface RectType {
  x1: number; y1: number; x2: number; y2: number;
}
interface ShapeDataType {
  location?: number[]; areaType?: AreaType; area?: any; _tmpArea?: any
}
// 计算得到包含该区域的最小矩形,得到左上角,右下角.
function calcMinRect({ location, areaType, area, _tmpArea }: ShapeDataType): RectType | undefined {
  if (!location) return undefined;
  switch (areaType) {
    case 'point': return { x1: location[0] - 5, y1: location[1] - 5, x2: location[0] + 5, y2: location[1] + 5 };
    case 'circle': {
      let radius = _tmpArea || area || 0;
      if (radius < 5) radius = 5;
      return { x1: location[0] - radius, y1: location[1] - radius, x2: location[0] + radius, y2: location[1] + radius }
    }
    case 'rectangle': {
      const point2 = _tmpArea || area;
      if (!point2) return { x1: location[0], y1: location[1], x2: location[0], y2: location[1] };
      const x = location[0] > point2[0] ? [point2[0], location[0]] : [location[0], point2[0]];
      const y = location[1] > point2[1] ? [point2[1], location[1]] : [location[1], point2[1]];
      return { x1: x[0], y1: y[0], x2: x[1], y2: y[1] };
    }
    case 'polygon': {
      const points = _tmpArea ? [...(area || []), _tmpArea] : area;
      if (!points || points.length === 0) return { x1: location[0], y1: location[1], x2: location[0], y2: location[1] };
      const { x, y } = points.reduce((prev: { x: number[]; y: number[] }, item: number[]) => {
        prev.x.push(item[0]);
        prev.y.push(item[1]);
        return prev;
      }, { x: [location[0]], y: [location[1]] })
      return { x1: min(x) || 0, y1: min(y) || 0, x2: max(x) || 0, y2: max(y) || 0 }
    }
    default: return undefined;
  }
}

function genShapeProps({ location, areaType, area, _tmpArea }: ShapeDataType, minRect: RectType): { [key: string]: any } {
  if (!location) return {};
  switch (areaType) {
    case 'point': return { cx: location[0] - minRect.x1, cy: location[1] - minRect.y1, r: 3 };
    case 'circle': return { cx: location[0] - minRect.x1, cy: location[1] - minRect.y1, r: _tmpArea || area || 0 };
    case 'rectangle': return { x: 0, y: 0, width: minRect.x2 - minRect.x1, height: minRect.y2 - minRect.y1 };
    case 'polygon': {
      const points1 = _tmpArea ? [...(area || []), _tmpArea] : area || [];
      const points = [location, ...points1].map(item => `${item[0] - minRect.x1},${item[1] - minRect.y1}`).join(' ');
      return { points };
    }
  }
  return {};
}

const shapeStyle = {
  fill: 'blue',
  stroke: 'pink',
  strokeWidth: 2,
  opacity: 0.5
}
interface NodePositionProps {
  data: Partial<PositionType> & { onChangeInner?: NodePositionInnerChangeHandler };
  [key: string]: any;
}
function NodePosition({ data }: NodePositionProps) {
  const { id, name, bg, location, areaType, area, onChangeInner } = data || {};
  const { image, matrixString } = bg || {};
  const bgStyle = React.useMemo<React.CSSProperties>(() => {
    return {
      left: 0,
      top: 0,
      transform: matrixString,
      transformOrigin: '0px 0px 0px',
      position: 'absolute',
    }
  }, [matrixString]);
  const refLocationIndicator = React.useRef<any>(undefined);
  const refAreaIndicators = React.useRef<any[]>([]);
  // const updateNodeInternals = useUpdateNodeInternals();
  React.useEffect(() => {
    if (!refLocationIndicator.current) {
      return;
    }
    if (!location) {
      return;
    }

    const selection = select(refLocationIndicator.current);
    const dragHandler = drag().on('drag', (evt) => {
      // const dx = evt.x - 100;
      // const dy = evt.y - 100;
      // const rad = Math.atan2(dx, dy);
      // const deg = rad * (180 / Math.PI);
      // setRotation(180 - deg);
      // updateNodeInternals(id);
      const evt2 = evt.sourceEvent;
      // console.log('dragHandler', {clientX: evt2?.clientX, clientY: evt2?.clientY}, evt);
      onChangeInner?.({ clientX: evt2?.clientX, clientY: evt2?.clientY, data, attr: 'location', attrIndex: 0 });
    });

    selection.call(dragHandler as any);
    return () => {
      selection.on('.drag', null);
    }
  }, [id, !location, onChangeInner]);
  React.useEffect(() => {
    if (!refAreaIndicators.current || refAreaIndicators.current.length < 1) {
      // 无area点.
      return;
    }
    if (!area || (area as any).length < 1) {
      return;
    }

    const selections: any = [];
    for (let i = 0; i < refAreaIndicators.current.length; i++) {
      const selection = select(refAreaIndicators.current[i]);
      const dragHandler = drag().on('drag', (evt) => {
        const evt2 = evt.sourceEvent;
        // console.log('dragHandler area', {clientX: evt2?.clientX, clientY: evt2?.clientY}, evt);
        onChangeInner?.({ clientX: evt2?.clientX, clientY: evt2?.clientY, data, attr: 'area', attrIndex: i });
      });
      selection.call(dragHandler as any);
      selections.push(selection);
    }
    return () => {
      for (let i = 0; i < selections.length; i++) {
        selections[i].on('.drag', null);
      }
    }
  }, [id, (area as any)?.length, onChangeInner]);

  // 为保证svg能显示,需要计算得到包含所有点的最小矩形
  const minRect = calcMinRect(data); // 最小矩形:{x1: 左上角x, y1: 左上角y, x2: 右下角x, y2: 右下角y}
  const w = minRect ? (minRect.x2 - minRect.x1) : 0;
  const h = minRect ? (minRect.y2 - minRect.y1) : 0;
  // 计算location相对于minRect起始点的偏移量.
  // 对于本组件来说,location是起始点,是传给xyflow的子节点的位置,xyflow会根据此位置进行偏移.
  // 所以,所有子元素都要相对于location去做偏移,location与minRect的偏移,代表了其他所有子元素的相对偏移.
  const x = minRect ? (minRect.x1 - location[0]) : 0;
  const y = minRect ? (minRect.y1 - location[1]) : 0;
  // 所有点为了在svg中,需要减掉(minRect.x1, minRect.y1).
  const svgStyle = {
    transform: `translate(${x}px, ${y}px)`,
  }
  console.log('NodePosition render', minRect, data.location, data.area);
  const shapeProps = minRect ? genShapeProps(data, minRect) : {};
  return (
    <div>
      {image ? <img src={image} style={bgStyle} alt='无背景图片' /> : null}
      <svg width={w} height={h} style={svgStyle}>
        {areaType === 'rectangle' ? (
          <rect {...shapeProps} style={shapeStyle} />
        ) : areaType === 'point' ? (
          <circle {...shapeProps} style={shapeStyle} />
        ) : areaType === 'circle' ? (
          <circle {...shapeProps} style={shapeStyle} />
        ) : areaType === 'polygon' ? (
          <polygon {...shapeProps} style={shapeStyle} />
        ) : null}
      </svg>
      <div style={{ position: 'absolute', left: 0, top: 0 }}>
        <label>{name || '未命名'}</label>
      </div>
      <>
        {location ? <div ref={refLocationIndicator} className='node-position-indicator'><Icon name='icon-resizer' stroke='yellow' strokeWidth={3} strokeDasharray={'2,4'} fill='none' /></div> : null}
        {(areaType === 'rectangle' && area) ? (
          <div ref={(node) => refAreaIndicators.current[0] = node} className='node-position-indicator' style={{ transform: `translate(${(area as number[])[0] - location[0]}px, ${(area as number[])[1] - location[1]}px)` }}><Icon name='icon-resizer' stroke='yellow' strokeWidth={3} strokeDasharray={'2,4'} fill='none' /></div>
        ) : null}
        {(areaType === 'polygon' && area) ? (area as number[][]).map((item: number[], index: number) => (
          <div ref={(node) => refAreaIndicators.current[index] = node} className='node-position-indicator' style={{ transform: `translate(${item[0] - location[0]}px, ${item[1] - location[1]}px)` }}><Icon name='icon-resizer' stroke='yellow' strokeWidth={3} strokeDasharray={'2,4'} fill='none' /></div>
        )) : null}

      </>
    </div>
  );
}

export default NodePosition;
